#include <linux/init.h>
#include <linux/types.h>
#include <linux/idr.h>
#include <linux/input/mt.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/major.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/poll.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/rcupdate.h>
#include <linux/cdev.h>  
#include <linux/interrupt.h> 
#include <linux/ioctl.h>  
#include <linux/gpio.h>  
#define DEVICE_NAME "ioinput"              //设备名称
#define IRQNUM1      (IRQ_GPIO_START+0x0103)//中断号  //GPIOP_I3
#define IRQNUM2      (IRQ_GPIO_START+0x0104)//中断号  //GPIO_PI4
#define IRQNUM3      (IRQ_GPIO_START+0x0105)//中断号  //GPIO_PI5
#define IRQNUM4     (IRQ_GPIO_START+0x0106)//中断号  //GPIO_PI6
struct conf
{
  unsigned char sample_io;
  unsigned char sample_io_enable;
  unsigned char action_io;

};



int light_major = 200; 
static struct class *firstdrv_class;  
static struct device *firstdrv_device;  
static volatile int ev_press = 0;
static DECLARE_WAIT_QUEUE_HEAD(io_waitq);

struct ioput_data {
	struct cdev cdev;  
	int vaild;  //
        int cnt;//计数
};
#define UP 1
#define DOWN 0
#define ENABLE 1
#define DISABLE 0

struct ioput_data *myinput;

unsigned char sample_status = 0;
unsigned char sample_flag = 0;
unsigned char action_flag = 0;
unsigned int cnt =0;
struct conf tps_conf={.sample_io=0,.sample_io_enable=0,.action_io=0};

static irqreturn_t ioinput_interrupt1(int irq, void *dev_id)
{
	struct ioput_data *io_irqs = (struct ioput_data *)dev_id;
	if(tps_conf.sample_io==1&&tps_conf.sample_io_enable==ENABLE&&tps_conf.action_io!=1)
	{
		if(gpio_get_value(NUC970_PI3)==1)//down - up
		{
			sample_status = UP;
			sample_flag = 1;
		}
		else 
		{
			if(sample_flag==1)
			{
				sample_status =DOWN;//up -down
				sample_flag=0;
				if(action_flag==1)
				{
					cnt++;
					action_flag = 0;
				}
				
			}			
		}
	}
    
    if(tps_conf.action_io==1&&tps_conf.sample_io_enable==DISABLE)
    {
		cnt++;
		
	}
	if(tps_conf.action_io==1&&tps_conf.sample_io!=1&&tps_conf.sample_io_enable==ENABLE)
	{
		action_flag = 1;
	}
	
	wake_up_interruptible(&io_waitq); /*唤醒等待队列*/
	ev_press = 1;

	//printk ("\tInterrupt11!\n");
	return IRQ_HANDLED;
}


static irqreturn_t ioinput_interrupt2(int irq, void *dev_id)
{
	struct ioput_data *io_irqs = (struct ioput_data *)dev_id;
	if(tps_conf.sample_io==2&&tps_conf.sample_io_enable==ENABLE&&tps_conf.action_io!=2)
	{
		if(gpio_get_value(NUC970_PI4)==1)//down - up
		{
			sample_status = UP;
			sample_flag = 1;
		}
		else 
		{
			if(sample_flag==1)
			{
				sample_status =DOWN;//up -down
				sample_flag=0;
				if(action_flag==1)
				{
					cnt++;
					action_flag = 0;
				}
				
			}			
		}
	}
    
    if(tps_conf.action_io==2&&tps_conf.sample_io_enable==DISABLE)
    {
		cnt++;
		
	}
	if(tps_conf.action_io==2&&tps_conf.sample_io!=2&&tps_conf.sample_io_enable==ENABLE)
	{
		action_flag = 1;
	}
	
	
	
	
	
	wake_up_interruptible(&io_waitq); /*唤醒等待队列*/
	ev_press = 2;

	//printk ("\tInterrupt222!\n");
	return IRQ_HANDLED;
}

static irqreturn_t ioinput_interrupt3(int irq, void *dev_id)
{
	struct ioput_data *io_irqs = (struct ioput_data *)dev_id;
	
	if(tps_conf.sample_io==3&&tps_conf.sample_io_enable==ENABLE&&tps_conf.action_io!=3)
	{
		if(gpio_get_value(NUC970_PI5)==1)//down - up
		{
			sample_status = UP;
			sample_flag = 1;
		}
		else 
		{
			if(sample_flag==1)
			{
				sample_status =DOWN;//up -down
				sample_flag=0;
				if(action_flag==1)
				{
					cnt++;
					action_flag = 0;
				}
				
			}			
		}
	}
    
    if(tps_conf.action_io==3&&tps_conf.sample_io_enable==DISABLE)
    {
		cnt++;
		
	}
	if(tps_conf.action_io==3&&tps_conf.sample_io!=3&&tps_conf.sample_io_enable==ENABLE)
	{
		action_flag = 1;
	}
	
	wake_up_interruptible(&io_waitq); /*唤醒等待队列*/
	ev_press = 3;

	//printk ("\tInterrupt333!\n");
	return IRQ_HANDLED;
}

static irqreturn_t ioinput_interrupt4(int irq, void *dev_id)
{
	struct ioput_data *io_irqs = (struct ioput_data *)dev_id;
	if(tps_conf.sample_io==4&&tps_conf.sample_io_enable==ENABLE&&tps_conf.action_io!=4)
	{
		if(gpio_get_value(NUC970_PI6)==1)//down - up
		{
			sample_status = UP;
			sample_flag = 1;
		}
		else 
		{
			if(sample_flag==1)
			{
				sample_status =DOWN;//up -down
				sample_flag=0;
				if(action_flag==1)
				{
					cnt++;
					action_flag = 0;
				}
				
			}			
		}
	}
    
    if(tps_conf.action_io==4&&tps_conf.sample_io_enable==DISABLE)
    {
		cnt++;
		
	}
	if(tps_conf.action_io==4&&tps_conf.sample_io!=4&&tps_conf.sample_io_enable==ENABLE)
	{
		action_flag = 1;
	}
	
	
	wake_up_interruptible(&io_waitq); /*唤醒等待队列*/
	ev_press = 4;

	//printk ("\tInterrupt444!\n");
	return IRQ_HANDLED;
}
unsigned long irqflag = 0x0;
unsigned char irq[4]={0,0,0,0};
#define IO1 (0x01)
#define IO2 (0x02+1)
#define IO3 (0x03+2)
#define IO4 (0x04+3)


//#define TYPE 's'
//#define IO0 _IOWR(TYPE,0x00,unsigned int)
//#define IO1 _IOWR(TYPE,0x01,unsigned int)
//#define IO2 _IOWR(TYPE,0x02,unsigned int)
//#define IO3 _IOWR(TYPE,0x03,unsigned int)
//#define IO4 _IOWR(TYPE,0x04,unsigned int)

/*IO操作*/
static long ioinput_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	int err =0;
	
	struct _test_t *dev = filp->private_data;

	irqflag = arg;
	printk("io=%d,arg=%ld\n",cmd,arg);
	//if(cmd==1)
		//cmd =1;
        //if(cmd==2)
		cmd=3;
	//if(cmd==3)
	//	cmd=5;
        //if(cmd==4)
 	 //       cmd=7;

	#if 1
	 switch(cmd)
    {
        case IO1:
        {
                
				err = request_irq(IRQNUM1, ioinput_interrupt1, irqflag, "ioinput", myinput);
				if (err)
				{
					/*如果出错，释放已经注册的中断，并返回*/
					disable_irq(IRQNUM1);
					free_irq(IRQNUM1, myinput);
							printk("request irq IRQNUM1 %d failed\n",IRQNUM1);
						
					return -EBUSY;
				}
			    irq[0]=1;
					
					
			return 0;
			
		}

        break;

        case IO2:
        		err = request_irq(IRQNUM2, ioinput_interrupt2, irqflag, "ioinput", myinput);
				if (err)
				{
					/*如果出错，释放已经注册的中断，并返回*/
					disable_irq(IRQNUM2);
					free_irq(IRQNUM2, myinput);
							printk("request irq IRQNUM1 %d failed\n",IRQNUM2);
						
					return -EBUSY;
				}
				irq[1]=1;
				
			return 0;

        break;
        case IO3:
        		err = request_irq(IRQNUM3, ioinput_interrupt3, irqflag, "ioinput", myinput);
				if (err)
				{
					/*如果出错，释放已经注册的中断，并返回*/
					disable_irq(IRQNUM3);
					free_irq(IRQNUM3, myinput);
							printk("request irq IRQNUM1 %d failed\n",IRQNUM3);
						
					return -EBUSY;
				}
					irq[2]=1;
				
			return 0;
        
        break;
        
        case IO4:
        
                err = request_irq(IRQNUM4, ioinput_interrupt4, irqflag, "ioinput", myinput);
				if (err)
				{
					/*如果出错，释放已经注册的中断，并返回*/
					disable_irq(IRQNUM4);
					free_irq(IRQNUM4, myinput);
							printk("request irq IRQNUM1 %d failed\n",IRQNUM4);
						
					return -EBUSY;
				}
					irq[3]=1;
			return 0;
        break;

        default:

            return -ENOTTY;
         break;
    }


    return -1;
        #endif
	
}
static int ioinput_open(struct inode *inode, struct file *file)
{
    
        // IRQF_TRIGGER_HIGH 0x00000004                  指定中断触发类型：高电平有效。新增加的标志 
	file->private_data = container_of(inode->i_cdev,struct ioput_data,cdev); 
        /*注册中断函数*/
       
	 ev_press = 1;
	/*正常返回*/
	return 0;
}
static int ioinput_close(struct inode *inode, struct file *file)
{
    if(irq[0]==1)
	{
			free_irq(IRQNUM1, myinput);
			irq[0]=0;
			
	}
	if(irq[1]==1)
	{
		free_irq(IRQNUM2, myinput);
		irq[1]=0;
	}
	if(irq[2]==1)  
	{
		free_irq(IRQNUM3, myinput);
		irq[2]=0;
	}
	if(	irq[3]==1)
	{
		free_irq(IRQNUM4, myinput);
		irq[3]=0;
	}
    

    return 0;
}
static int ioinput_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
	unsigned long err=0;

	struct ioput_data *dev = filp->private_data; 

	if (!ev_press)
	{
		if (filp->f_flags & O_NONBLOCK)
		    /*当中断标识为0 时，并且该设备是以非阻塞方式打开时，返回*/
		    return -EAGAIN;
		else
		    /*当中断标识为0 时，并且该设备是以阻塞方式打开时，进入休眠状态，等待被唤醒*/
		    wait_event_interruptible(io_waitq, ev_press);
	}

	/*把中断标识清零*/
	//ev_press = 0;

	/*传递到用户空间*/
	err = copy_to_user(buff, (const void *)&cnt, sizeof(int));
	if(err!=0)
	  return -1;
	printk("  kernel cnt =%d \n",cnt);

	ev_press = 0;


	return 0;
}
static int ioinput_write(struct file *filp,const char __user *buff, size_t count, loff_t *offp)
{
	unsigned long err=0;
	char buf[10]={0};
	struct ioput_data *dev = filp->private_data; 
	err = copy_from_user(buf, buff, 4);

    printk("kernel===%d\n",buf[0]);
	if(err!=0)
	  return -1;
	/*设置到cnt*/
	if(buf[0]==0x01)
	{
	  cnt = 0;	
	}
	if(buf[0]==0x02)
	{
	   memset(&tps_conf.sample_io,0x00,sizeof(struct conf));	
	   memcpy(&tps_conf.sample_io,&buf[1],3);
	}

	return 0;
}
static unsigned int ioinput_poll( struct file *file, struct poll_table_struct *wait)
{
    unsigned int mask = 0;

    /*把调用poll 或者select 的进程挂入队列，以便被驱动程序唤醒*/
    poll_wait(file, &io_waitq, wait);

    if (ev_press)  //中断事件发生，这时有数据可读，在mask中标记是可读事件发生
        mask |= POLLIN | POLLRDNORM;

    return mask;
}
/*设备操作集*/
static struct file_operations dev_fops = {
    .owner = THIS_MODULE,
    .open = ioinput_open,
    .release = ioinput_close,
    .read = ioinput_read,
    .write = ioinput_write,
    .poll = ioinput_poll,
    .unlocked_ioctl = ioinput_ioctl,
};

static void ioinput_setup_cdev(struct ioput_data *dev,int index)  
{  
    int err,devno = MKDEV(light_major,index);  
  
    cdev_init(&dev->cdev,&dev_fops);  
    dev->cdev.owner = THIS_MODULE;  
    dev->cdev.ops = &dev_fops;  
  
    err = cdev_add(&dev->cdev,devno,1);  
  
    if(err)  
    {  
        printk(KERN_NOTICE "Error %d adding LED%d",err,index);  
    }  
} 
int ioinput_init(void)  
{  
    int result=0;  

    printk("ioinput dev init... ..\n");
  
    dev_t dev = MKDEV(light_major,0);  
    
   


    if(light_major)  
    {  
          
        result = register_chrdev_region(dev,1,"io_input");  
	/* io_input */  
	firstdrv_class = class_create(THIS_MODULE, "ioinput");  

	/* 在ioinput类下创建设备，供应用程序打开设备*/  
	firstdrv_device = device_create(firstdrv_class, NULL, MKDEV(light_major, 0), NULL, DEVICE_NAME);
    }  
  
    if(result < 0)  
    {  
        return result;  
    }  
  
    myinput = kmalloc(sizeof(struct ioput_data),GFP_KERNEL);  
    if(!myinput)  
    {  
        result = - ENOMEM;  
        unregister_chrdev_region(dev,1);   
	return result;
    }  
    memset(myinput,0,sizeof(struct ioput_data));  
    ioinput_setup_cdev(myinput,0);  
    printk("ioinput dev init ok\n");
    return 0;  
} 

/*注销设备*/
static void __exit dev_exit(void)
{
	
   	printk("ioinput dev exit ....\n");
	cdev_del(&myinput->cdev);     
	kfree(myinput);  
	unregister_chrdev_region(MKDEV(light_major,0),1); 
	device_unregister(firstdrv_device);  //卸载类下的设备  
   	class_destroy(firstdrv_class);      //卸载类  
    	printk("ioinput dev exit ok\n");
}
module_init(ioinput_init); //模块初始化，仅当使用insmod/podprobe 命令加载时有用，如果设备不是通过模块方式加载，此处将不会被调用

module_exit(dev_exit); //卸载模块，当该设备通过模块方式加载后，可以通过rmmod 命令卸载，将调用此函数

MODULE_LICENSE("GPL");//版权信息

MODULE_AUTHOR("Madest Inc.");//作者名字


